#include "ilp_handler.h"
#include <sstream>
#include <iostream>
#include <chrono>
#include <cstdlib>

namespace ilp {

static bool parse_tags(const std::string& tags_str, std::map<std::string, std::string>* tags) {
    if (tags_str.empty()) return true;

    size_t i = 0;
    while (i < tags_str.length()) {
        // Find the key
        std::string key;
        while (i < tags_str.length() && tags_str[i] != '=') {
            if (tags_str[i] == '\\' && i + 1 < tags_str.length()) {
                // Escaped character in key
                i++; // Skip the backslash
                if (i < tags_str.length()) {
                    key += tags_str[i];
                    i++;
                }
            } else {
                key += tags_str[i];
                i++;
            }
        }

        if (i >= tags_str.length() || tags_str[i] != '=') {
            return false; // No '=' found
        }
        i++; // Skip '='

        // Find the value
        std::string value;
        while (i < tags_str.length() && tags_str[i] != ',') {
            if (tags_str[i] == '\\' && i + 1 < tags_str.length()) {
                // Escaped character in value
                i++; // Skip the backslash
                if (i < tags_str.length()) {
                    value += tags_str[i];
                    i++;
                }
            } else {
                value += tags_str[i];
                i++;
            }
        }

        (*tags)[key] = value;

        if (i < tags_str.length() && tags_str[i] == ',') {
            i++; // Skip comma
        }
    }
    return true;
}

static bool parse_fields(const std::string& fields_str, std::map<std::string, double>* fields) {
    if (fields_str.empty()) return false;  // At least one field is required

    std::istringstream iss(fields_str);
    std::string pair;
    while (std::getline(iss, pair, ',')) {
        size_t eq = pair.find('=');
        if (eq == std::string::npos) return false;

        std::string key = pair.substr(0, eq);
        std::string value = pair.substr(eq + 1);

        // Parse value (support integer, float, boolean)
        double val = 0.0;

        // Remove trailing 'i' for integer values
        if (!value.empty() && value.back() == 'i') {
            value.pop_back();
        }

        // Handle boolean
        if (value == "true" || value == "t" || value == "T") {
            val = 1.0;
        } else if (value == "false" || value == "f" || value == "F") {
            val = 0.0;
        } else {
            // Parse as number
            char* end;
            val = std::strtod(value.c_str(), &end);
            if (end == value.c_str()) {
                // Failed to parse - might be a string field, skip for now
                continue;
            }
        }

        (*fields)[key] = val;
    }
    return !fields->empty();
}

bool parse_ilp_line(const std::string& line, ILPPoint* point) {
    if (line.empty() || line[0] == '#') {
        return false;  // Empty line or comment
    }

    // Debug logging
    std::cout << "[ILP_DEBUG] Parsing line: " << line << std::endl;

    // Find the first unescaped space (separates measurement+tags from fields)
    size_t first_space = std::string::npos;
    for (size_t i = 0; i < line.length(); i++) {
        if (line[i] == ' ' && (i == 0 || line[i-1] != '\\')) {
            first_space = i;
            break;
        }
    }

    if (first_space == std::string::npos) {
        std::cout << "[ILP_DEBUG] No unescaped space found, invalid format" << std::endl;
        return false;
    }

    // Parse measurement and tags
    std::string measurement_tags = line.substr(0, first_space);

    // Find first unescaped comma to separate measurement from tags
    size_t comma = std::string::npos;
    for (size_t i = 0; i < measurement_tags.length(); i++) {
        if (measurement_tags[i] == ',' && (i == 0 || measurement_tags[i-1] != '\\')) {
            comma = i;
            break;
        }
    }

    if (comma == std::string::npos) {
        // No tags
        point->measurement = measurement_tags;
        std::cout << "[ILP_DEBUG] Measurement: " << point->measurement << ", no tags" << std::endl;
    } else {
        point->measurement = measurement_tags.substr(0, comma);
        std::string tags_str = measurement_tags.substr(comma + 1);
        std::cout << "[ILP_DEBUG] Measurement: " << point->measurement << ", tags_str: " << tags_str << std::endl;
        if (!parse_tags(tags_str, &point->tags)) {
            std::cout << "[ILP_DEBUG] Failed to parse tags" << std::endl;
            return false;
        }
        std::cout << "[ILP_DEBUG] Tags parsed successfully, count: " << point->tags.size() << std::endl;
    }

    // Find the second unescaped space (separates fields from timestamp)
    size_t second_space = std::string::npos;
    for (size_t i = first_space + 1; i < line.length(); i++) {
        if (line[i] == ' ' && (i == 0 || line[i-1] != '\\')) {
            second_space = i;
            break;
        }
    }

    std::string fields_str;
    if (second_space == std::string::npos) {
        // No timestamp, use current time
        fields_str = line.substr(first_space + 1);
        point->timestamp_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
            std::chrono::system_clock::now().time_since_epoch()
        ).count();
    } else {
        fields_str = line.substr(first_space + 1, second_space - first_space - 1);
        std::string timestamp_str = line.substr(second_space + 1);

        // Parse timestamp
        char* end;
        int64_t ts = std::strtoll(timestamp_str.c_str(), &end, 10);
        if (end == timestamp_str.c_str()) {
            return false;
        }

        // Timestamp could be in different precisions
        // If it's too small, it's probably in seconds
        if (ts < 1000000000000LL) {  // Less than year 2001 in ms
            ts *= 1000000000LL;  // Convert seconds to nanoseconds
        } else if (ts < 1000000000000000LL) {  // Less than year 2001 in us
            ts *= 1000000LL;  // Convert milliseconds to nanoseconds
        } else if (ts < 1000000000000000000LL) {  // Less than year 2001 in ns
            ts *= 1000LL;  // Convert microseconds to nanoseconds
        }
        // Otherwise assume it's already in nanoseconds

        point->timestamp_ns = ts;
    }

    // Parse fields
    if (!parse_fields(fields_str, &point->fields)) {
        return false;
    }

    return true;
}

bool parse_ilp_batch(const std::string& data, std::vector<ILPPoint>* points) {
    points->clear();

    std::istringstream iss(data);
    std::string line;

    while (std::getline(iss, line)) {
        // Remove trailing \r if present
        if (!line.empty() && line.back() == '\r') {
            line.pop_back();
        }

        if (line.empty() || line[0] == '#') {
            continue;  // Skip empty lines and comments
        }

        ILPPoint point;
        if (parse_ilp_line(line, &point)) {
            points->push_back(point);
        }
    }

    return !points->empty();
}

} // namespace ilp